﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Space_Conquest 
{

    /// <summary>
    /// Different layers that units can be on
    /// </summary>
    enum UnitLayer
    {
        Ground,
        Air
    }

    /// <summary>
    /// Possible directions a movable unit can face
    /// </summary>
    enum UnitDirection
    {
        Up = 0,
        Right = 1,
        Down = 2,
        Left = 3
    }

    /// <summary>
    /// Possible states a unit can be in
    /// </summary>
    enum UnitState
    {
        MoveToTile,
        MoveStep,
        Gather,
        GatherWork,
        Attack,
        Attacking,
        Build,
        Building,
        Paralyzed
    }

    /// <summary>
    /// Extention methods to make Motion() code more readable
    /// </summary>
    static class UnitStateExtentions
    {

        public static bool UnitIsStill(this Stack<UnitState> unitStates)
        {
            return (unitStates.Count == 0);
        }

        public static bool UnitIsMoving(this Stack<UnitState> unitStates)
        {
            return (unitStates.Peek() == UnitState.MoveStep || unitStates.Peek() == UnitState.MoveToTile);
        }

    }

    abstract class Unit : IRender, IMotion, IInput
    {

        #region Constructors

        public Unit(Texture texture, int x, int y, Player owner)
        {

            _texture = texture;

            _area.X = x;
            _area.Y = y;

            _sprite = new Sprite((x * Globals.TILE_SIZE), (y * Globals.TILE_SIZE), Globals.TILE_SIZE, Globals.TILE_SIZE);
            _owner = owner;

            _sprite.Left = _area.X * Globals.TILE_SIZE;
            _sprite.Top = _area.Y * Globals.TILE_SIZE;

            _isAlive = true;

        }

        #endregion

        #region Properties

        public int HitPoints
        {
            get 
            {
                return _hitPoints; 
            }
        }

        public int MaxHitPoints
        {
            get
            {
                return _maxHitPoints;
            }
        }

        public bool IsAlive
        {
            get
            {
                return _isAlive;
            }
        }

        public int Y
        {
            get
            {
                return _area.Y;
            }
        }

        public int X
        {
            get
            {
                return _area.X;
            }
        }

        public int SpriteX
        {
            get
            {
                return _sprite.Left;
            }
        }

        public int SpriteY
        {
            get
            {
                return _sprite.Top;
            }
        }

        public int SpriteWidth
        {
            get
            {
                return _sprite.Width;
            }
        }

        public int SpriteHeight
        {
            get
            {
                return _sprite.Height;
            }
        }

        public int Width
        {
            get
            {
                return _area.Width;
            }
        }

        public int Height
        {
            get
            {
                return _area.Height;
            }
        }

        public int VisionRange
        {
            get
            {
                return _visionRange;
            }
        }

        public MapArea MapArea
        {
            get
            {
                return _area;
            }
        }

        public MapPoint MapPoint
        {
            get
            {
                return _area.GetScalarPoint();
            }
        }

        public Player Owner
        {
            get
            {
                return _owner;
            }
        }

        public static UnitManager Parent
        {
            get
            {
                return _parent;
            }
            set
            {
                _parent = value;
            }
        }


        #endregion 

        #region Methods

        public virtual void Render()
        {
        }

        public virtual void Motion()
        {
        }

        public virtual bool MouseDown(MouseButtons button, int x, int y)
        {
            return false;
        }

        public virtual bool MouseUp(MouseButtons button, int x, int y)
        {
            return false;
        }

        public virtual bool MouseMove(int x, int y)
        {
            return false;
        }

        public virtual bool KeyDown(Keys key, bool shift)
        {
            return false;
        }

        public virtual bool KeyUp(Keys key, bool shift)
        {
            return false;
        }

        public virtual void TakeHit(int damage)
        {

            if(!(this is ResourceUnit))
            {
                List<MapPoint> points = MapArea.GetListOfAllMapPoints();
                Parent.Parent.AnimationManager.DisplayAnimation(AnimationType.TakeHit, 
                                                                RenderLayer.AboveGroundUnitsBelowAirUnits,
                                                                points[new Random().Next(0,points.Count)]);
            }

            if (_hitPoints - damage > 0)
            {
                // Still alive , subtract damage
                _hitPoints -= damage;
            }
            else
            {
                // Die
                _hitPoints = 0;
                DestroyUnit();
            }

        }

        public virtual void TakeHit(int damage, Unit from)
        {

            TakeHit(damage);

        }

        public MapPoint GetClosestAdjacentAvailablePoint(MapPoint sourcePoint)
        {

            Map map = Parent.Parent.Map;

            List<MapPoint> adjacentPoints = MapArea.GetListOfAdjacentMapPoints();

            MapPoint returnPoint = null;
            double minimumDistance = 100000;

            foreach (MapPoint point in adjacentPoints)
            {

                if (sourcePoint.DistanceBetween(point) < minimumDistance && map.TileIsPassable(_layer, point))
                {
                    minimumDistance = sourcePoint.DistanceBetween(point);
                    returnPoint = new MapPoint(point.X, point.Y);
                }

            }

            return returnPoint;


        }

        private void DestroyUnit()
        {

            _isAlive = false;
            UnsetLocation(_layer, _area);
            Parent.Parent.UnitManager.RemoveUnit(this);

        }

        protected bool TrySetLocation(UnitLayer layer, int xTile, int yTile)
        {
            return TrySetLocation(layer, xTile, yTile, 1, 1);
        }

        protected bool TrySetLocation(UnitLayer layer, int xTile, int yTile, int xWide, int yHigh)
        {

            bool canSet = true;
            Unit[,] layerMap = null;
            Map map = Parent.Parent.Map;

            // Figure out which layer unit is on
            if (layer == UnitLayer.Air)
                layerMap = map.AirMap;
            else if (layer == UnitLayer.Ground)
                layerMap = map.GroundMap;


            for (int y = yTile; y < (yTile + yHigh); y++)
            {
                for (int x = xTile; x < (xTile + xWide); x++)
                {
                    if (!map.TileIsPassable(layer, x, y))
                    {
                        canSet = false;
                    }
                }
            }

            if (canSet)
            {

                _area.X= xTile;
                _area.Y = yTile;

                for (int y = yTile; y < (yTile + yHigh); y++)
                    for (int x = xTile; x < (xTile + xWide); x++)
                        layerMap[x, y] = this;
            }

            return canSet;

        }

        protected void UnsetLocation(UnitLayer layer, int xTile, int yTile)
        {
            UnsetLocation(layer, xTile, yTile, 1, 1);
        }

        protected void UnsetLocation(UnitLayer layer, MapArea area)
        {
            UnsetLocation(layer, area.X, area.Y, area.Width, area.Height);
        }

        protected void UnsetLocation(UnitLayer layer, int xTile, int yTile, int xWide, int yHigh)
        {

            Unit[,] layerMap = null;

            // Figure out which layer unit is on
            if (layer == UnitLayer.Air)
                layerMap = Parent.Parent.Map.AirMap;
            else if (layer == UnitLayer.Ground)
                layerMap = Parent.Parent.Map.GroundMap;

            // Remove unit from that layer
            for (int y = yTile; y < (yTile + yHigh); y++)
                for (int x = xTile; x < (xTile + xWide); x++)
                    layerMap[x, y] = null;

        }

        protected void SetLocation()
        {

            _sprite.Width = _area.Width * Globals.TILE_SIZE;
            _sprite.Height = _area.Height * Globals.TILE_SIZE;

            TrySetLocation(_layer, _area.X, _area.Y, _area.Width, _area.Height);
        }

        protected void RenderLifeBar()
        {

            _barSprite.Alpha = 150;
            _barSprite.Left = _sprite.Left; _barSprite.Top = _sprite.Top + 4;
            _barSprite.Height = 4;
            _barSprite.Width = (int)((double)(_sprite.Width) * ((double)_hitPoints / (double)_maxHitPoints));
            if (_barSprite.Width <= 0)
                _barSprite.Width = 1;
            _barSprite.Render(_colorBars[_owner.ID]);

            _barSprite.Alpha = 255;
            _barSprite.Top -= 4;
            _barSprite.Width = _sprite.Width; _barSprite.Height = 12;
            _barSprite.Render(_unitBar);

        }

        #endregion

        #region Private Variables

        /// <summary>
        /// Private variables
        /// </summary>



        protected static UnitManager _parent;

        protected static Sprite _barSprite = new Sprite(0, 0, 64, 64);
        protected static Texture _unitBar = new Texture("Images/UnitBar.bmp");
        protected static Texture[] _colorBars = new Texture[] {
                                                          new Texture("Images/RedBar.bmp"),
                                                          new Texture("Images/BlueBar.bmp"),
                                                          new Texture("Images/GreenBar.bmp"),
                                                          new Texture("Images/PurpleBar.bmp")
                                                            };

        protected Texture _texture;
        protected Sprite _sprite;

        protected Player _owner;

        protected MapArea _area = new MapArea(0,0,1,1);
        protected UnitLayer _layer;

        protected int _maxHitPoints;
        protected int _hitPoints;
        protected int _visionRange;

        protected bool _isAlive;

        #endregion

    }
}
